package com.atguigu.gmall.order.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.atguigu.gmall.common.constant.MqConst;
import com.atguigu.gmall.common.service.RabbitService;
import com.atguigu.gmall.common.util.HttpClientUtil;
import com.atguigu.gmall.model.enums.OrderStatus;
import com.atguigu.gmall.model.enums.ProcessStatus;
import com.atguigu.gmall.model.order.OrderDetail;
import com.atguigu.gmall.model.order.OrderInfo;
import com.atguigu.gmall.order.mapper.OrderDetailMapper;
import com.atguigu.gmall.order.mapper.OrderInfoMapper;
import com.atguigu.gmall.order.service.OrderService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
@Slf4j
public class OrderServiceImpl extends ServiceImpl<OrderInfoMapper,OrderInfo> implements OrderService {

    @Autowired
    private OrderInfoMapper orderInfoMapper;

    @Autowired
    private OrderDetailMapper orderDetailMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    @Value("${ware.url}")
    private String wareUrl;  //wareUrl= http://localhost:9001

    @Autowired
    private RabbitService rabbitService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long saveOrderInfo(OrderInfo orderInfo) {
        try {
            //  order_info
            //  total_amount order_status user_id out_trade_no trade_body operate_time expire_time process_status
            //  单价*数据相加；订单明细集合;
            orderInfo.sumTotalAmount();
            orderInfo.setOrderStatus(OrderStatus.UNPAID.name());
            //  赋值第三方交易编号：
            String outTradeNo = "ATGUIGU"+System.currentTimeMillis()+new Random().nextInt(1000);
            orderInfo.setOutTradeNo(outTradeNo);
            //  订单主体
            orderInfo.setTradeBody("电商支付-买个帽子");
            orderInfo.setOperateTime(new Date());
            //  订单的过期时间
            Calendar calendar = Calendar.getInstance();
            calendar.add(Calendar.DATE,1);
            orderInfo.setExpireTime(calendar.getTime());
            //  订单的进度状态
            orderInfo.setProcessStatus(ProcessStatus.UNPAID.name());
            orderInfoMapper.insert(orderInfo);
            //  订单明细：
            List<OrderDetail> orderDetailList = orderInfo.getOrderDetailList();
            if (!CollectionUtils.isEmpty(orderDetailList)){
                for (OrderDetail orderDetail : orderDetailList) {
                    //  赋值订单Id
                    orderDetail.setOrderId(orderInfo.getId());
                    orderDetailMapper.insert(orderDetail);
                }
            }
            //  获取订单Id
            Long orderId = orderInfo.getId();
            log.info("完成订单保存...");
            //  发送延迟消息：
            //  this.rabbitService.sendMsg();   发送普通消息，
            this.rabbitService.sendDelayMsg(MqConst.EXCHANGE_DIRECT_ORDER_CANCEL,MqConst.ROUTING_ORDER_CANCEL,orderId,MqConst.DELAY_TIME);
            //  返回订单
            return orderId;
        } catch (Exception e) {
            log.error("系统日志.."+e.getLocalizedMessage());
            throw new RuntimeException(e);
        }


    }

    @Override
    public String getTradeNo(String userId) {
        //  生成一个流水号
        String tradeNo = UUID.randomUUID().toString();
        //  声明个缓存的key
        String tradeNoKey = "user:"+userId+":tradeNo";
        //  存储缓存
        this.redisTemplate.opsForValue().set(tradeNoKey,tradeNo,10, TimeUnit.MINUTES);
        //  返回
        return tradeNo;
    }

    @Override
    public Boolean checkTradeNo(String userId, String tradeNo) {
        //  声明个缓存的key
        String tradeNoKey = "user:"+userId+":tradeNo";
        String redisTradeNo = (String) this.redisTemplate.opsForValue().get(tradeNoKey);
        //  返回比较结果
        return tradeNo.equals(redisTradeNo);
    }

    @Override
    public void delTradeNo(String userId) {
        //  删除缓存key
        redisTemplate.delete("user:"+userId+":tradeNo");
    }

    @Override
    public Boolean checkStock(Long skuId, Integer skuNum) {
        // http://localhost:9001/hasStock?skuId=10221&num=2 0：无库存   1：有库存
        // wareUrl= http://localhost:9001
        String result = HttpClientUtil.doGet(wareUrl + "/hasStock?skuId=" + skuId + "&num=" + skuNum);
        //  返回结果
        return "1".equals(result);
    }

    @Override
    public IPage<OrderInfo> getMyOrder(Page<OrderInfo> orderInfoPage, String userId, String orderStatus) {
        //  调用mapper
        IPage<OrderInfo> iPage = orderInfoMapper.selectMyOrder(orderInfoPage,userId,orderStatus);
        //  循环遍历
        iPage.getRecords().forEach(orderInfo -> {
            //  赋值状态名称
            orderInfo.setOrderStatusName(OrderStatus.getStatusNameByStatus(orderInfo.getOrderStatus()));
        });
        //  返回数据
        return iPage;
    }

    /**
     * 取消订单
     * @param orderId
     */
    @Override
    public void execExpiredOrder(Long orderId) {
        //  取消订单本质：
        //        OrderInfo orderInfo = new OrderInfo();
        //        orderInfo.setId(orderId);
        //        orderInfo.setOrderStatus(OrderStatus.CLOSED.name());
        //        orderInfo.setProcessStatus(ProcessStatus.CLOSED.name());
        //        this.orderInfoMapper.updateById(orderInfo);

        //  根据订单Id 修改订单状态！
        this.updateOrderStatus(orderId,ProcessStatus.CLOSED);

        //  应该也会关闭交易记录状态. paymentInfo; 使用mq 异步形式关闭交易记录.
        this.rabbitService.sendMsg(MqConst.EXCHANGE_DIRECT_PAYMENT_CLOSE,MqConst.ROUTING_PAYMENT_CLOSE,orderId);
        //  根据订单Id 将状态修改为支付
        //  this.updateOrderStatus(orderId,ProcessStatus.PAID);
    }

    @Override
    public OrderInfo getOrderInfo(Long orderId) {
        //  获取订单对象
        OrderInfo orderInfo = orderInfoMapper.selectById(orderId);

        if (orderInfo!=null){
            List<OrderDetail> orderDetailList = this.orderDetailMapper.selectList(new LambdaQueryWrapper<OrderDetail>().eq(OrderDetail::getOrderId, orderId));
            orderInfo.setOrderDetailList(orderDetailList);
        }
        //  返回orderInfo
        return orderInfo;
    }

    /**
     * 根据订单Id 修改订单状态！
     * @param orderId
     * @param processStatus
     */
    public void updateOrderStatus(Long orderId, ProcessStatus processStatus) {
        //  创建对象
        OrderInfo orderInfo = new OrderInfo();
        orderInfo.setId(orderId);
        orderInfo.setOrderStatus(processStatus.getOrderStatus().name());
        orderInfo.setProcessStatus(processStatus.name());
        this.orderInfoMapper.updateById(orderInfo);
    }

    @Override
    public void sendDeductStockMsg(Long orderId) {
        //  更新当前订单状态.
        this.updateOrderStatus(orderId,ProcessStatus.NOTIFIED_WARE);
        //  构建发送消息的内容 Json; orderInfo + orderDetail;
        OrderInfo orderInfo = this.getOrderInfo(orderId);
        //  将orderInfo 中的部分字段，封装到Map中！
        Map<String,Object> map = this.initWare(orderInfo);
        //  map对象就是我们想要的json 数据
        this.rabbitService.sendMsg(MqConst.EXCHANGE_DIRECT_WARE_STOCK,MqConst.ROUTING_WARE_STOCK, JSON.toJSONString(map));
    }

    /**
     * 将orderInfo 中的部分数据转换为map集合
     * @param orderInfo
     * @return
     */
    public Map<String, Object> initWare(OrderInfo orderInfo) {
        //  声明一个map 集合
        Map<String, Object> mapResult = new HashMap<>();
        mapResult.put("orderId",orderInfo.getId());
        mapResult.put("consignee", orderInfo.getConsignee());
        mapResult.put("consigneeTel", orderInfo.getConsigneeTel());
        mapResult.put("orderComment", orderInfo.getOrderComment());
        mapResult.put("orderBody", orderInfo.getTradeBody());
        mapResult.put("deliveryAddress", orderInfo.getDeliveryAddress());
        mapResult.put("paymentWay", "2");
        //  专门给拆单使用。
        mapResult.put("wareId", orderInfo.getWareId());

        /*
        details:[{skuId:101,skuNum:1,skuName:’小米手64G’},{skuId:201,skuNum:1,skuName:’索尼耳机’}]
         */
        List<OrderDetail> orderDetailList = orderInfo.getOrderDetailList();
        List<HashMap<String, Object>> hashMapList = orderDetailList.stream().map(orderDetail -> {
            //  构建一个map 集合
            HashMap<String, Object> map = new HashMap<>();
            map.put("skuId", orderDetail.getSkuId());
            map.put("skuNum", orderDetail.getSkuNum());
            map.put("skuName", orderDetail.getSkuName());
            //  返回这个集合
            return map;
        }).collect(Collectors.toList());
        //  赋值订单明细：
        mapResult.put("details",hashMapList);
        //  返回map 集合
        return mapResult;
    }

    /**
     * 获取到orderInfo 子订单
     * @param orderId
     * @param wareSkuMap
     * @return
     */
    @Override
    public List<OrderInfo> orderSplit(String orderId, String wareSkuMap) {
        //  创建子订单集合
        List<OrderInfo> orderInfoList = new ArrayList<>();
        /*
        1.  获取原始订单
        2.  wareSkuMap = [{"wareId":"1","skuIds":["22"]},{"wareId":"2","skuIds":["23","24"]}] 转化为JavaObject
        3.  创建一个子订单并给子订单赋值。
        4.  保存子订单
        5.  将子订单添加到集合中
        6.  修改原始订单状态.
         */
        OrderInfo orderInfoOrigin = this.getOrderInfo(Long.parseLong(orderId));
        //  转换数据
        List<Map> mapList = JSONObject.parseArray(wareSkuMap, Map.class);
        //  循环遍历集合
        if (!CollectionUtils.isEmpty(mapList)){
            mapList.forEach(map -> {
                //  获取仓库Id
                String wareId = (String) map.get("wareId");
                List<String> skuIdList = (List<String>) map.get("skuIds");
                //  创建子订单
                OrderInfo subOrderInfo = new OrderInfo();

                BeanUtils.copyProperties(orderInfoOrigin,subOrderInfo);
                //  赋值仓库Id
                subOrderInfo.setWareId(wareId);
                //  指定父级订单Id
                subOrderInfo.setParentOrderId(Long.parseLong(orderId));
                //  属性拷贝：
                //  子订单Id 设置为null 防止主键冲突
                subOrderInfo.setId(null);
                //  计算子订单的金额; 获取到子订单明细即可.
                //  这个集合中 22,23,24
                List<OrderDetail> orderDetailList = orderInfoOrigin.getOrderDetailList();
                //  利用filter 获取到子订单明细集合
                List<OrderDetail> detailList = orderDetailList.stream().filter(orderDetail -> skuIdList.contains(orderDetail.getSkuId().toString())).collect(Collectors.toList());
                subOrderInfo.setOrderDetailList(detailList);
                subOrderInfo.sumTotalAmount();

                //  保证子订单
                this.saveOrderInfo(subOrderInfo);
                //  将子订单添加到集合
                orderInfoList.add(subOrderInfo);
            });
        }
        //  修改原始订单状态.
        this.updateOrderStatus(Long.parseLong(orderId),ProcessStatus.SPLIT);
        //  返回子订单集合
        return orderInfoList;
    }

    @Override
    public void execExpiredOrder(Long orderId, String flag) {

        //  根据订单Id 修改订单状态！
        this.updateOrderStatus(orderId,ProcessStatus.CLOSED);
        //  判断flag
        if ("2".equals(flag)){
            //  应该也会关闭交易记录状态. paymentInfo; 使用mq 异步形式关闭交易记录.
            this.rabbitService.sendMsg(MqConst.EXCHANGE_DIRECT_PAYMENT_CLOSE,MqConst.ROUTING_PAYMENT_CLOSE,orderId);
        }
    }
}
